from twisted.python import log

import GameState
from ExtTypes import Enum
import Character
import Color
import Interpreter

state_handlers = {} # yeah this is risky..

states = Enum((
    "NOT_LOGGED_IN",
    "HAS_USERNAME",
    "PLAYING",
    "NAMING_NEW_CHARACTER",
    "RENAMING_NEW_CHARACTER",
    "SETTING_PASSWORD"
    ))

class StatefulConnection(object):

    def __init__(self, transport, init_state=states.NOT_LOGGED_IN):
        self.prompt = None
        self.__state = None
        self.state = init_state
        self.transport = transport
        self.do_prompt()

    def statehandler(state):
        """Decorator that adds the function to the state_handlers
        dictionary"""
        def _set_state_wrapper(function):
            state_handlers[state] = function
            return function
        return _set_state_wrapper

    # Set up the state property
    def get_state(self):
        return self.__state

    def set_state(self, value):
        if value in states:
            self.__state = value
            self.prompt = self.get_prompt(self.__state)
        else:
            raise ValueError

    state = property(get_state, set_state)
    # --------------------------

    def is_state(self, state):
        return self.state == state

    def receive_line(self, line):
        state_handlers.get(self.state)(self,line)

    @statehandler(states.PLAYING)
    def playing_handler(self, line):
        try:
            Interpreter.interpret(self.user,line)
        except Interpreter.CommandNotFound:
            self.to_self("Huh?")

    @statehandler(states.NOT_LOGGED_IN)
    def not_logged_in_handler(self, line):
        if line.upper() == "NEW":
            self.state = states.NAMING_NEW_CHARACTER
        else:
            self.username = line
            if GameState.is_logged_in(self.username):
                self.do_login_failure(
                "The user you are attempting to use is already logged in. Try again with a different user.\n")
                return
            else:
                self.state = states.HAS_USERNAME
        self.do_prompt()

    @statehandler(states.HAS_USERNAME)
    def has_username_handler(self, line):
        password = line
        try:
            self.user = Character.load(self.username, password)
            self.user.connection = self
            self.state = states.PLAYING
            self.do_prompt()
        except Character.InvalidCredentials:
            self.do_login_failure("Invalid credentials, try again!")
        except Character.DoesNotExist:
            self.do_login_failure("A Character with that username does not exist, try again!")

    @statehandler(states.NAMING_NEW_CHARACTER)
    @statehandler(states.RENAMING_NEW_CHARACTER)
    def naming_new_character_handler(self, line):
        if not Character.check_name(line):
            self.username = line.title()
            self.state = states.SETTING_PASSWORD
        else:
            self.state = states.RENAMING_NEW_CHARACTER
        self.do_prompt()

    @statehandler(states.SETTING_PASSWORD)
    def setting_password_handler(self, line):
        user = Character.load(self.username, line, new=True)
        self.user = user
        self.user.connection = self
        self.state = states.PLAYING
        self.do_prompt()

    def do_login_failure(self, reason, try_again=True):
        self.to_self(reason)
        if try_again:
            self.state = states.NOT_LOGGED_IN    
        else:
            self.die()

    def get_prompt(self, state):
        if state == states.NOT_LOGGED_IN:
            return "Type NEW or your username: "
        if state == states.HAS_USERNAME:
            return "password: "
        if state == states.PLAYING:
            return ">>> "
        if state == states.NAMING_NEW_CHARACTER:
            return "Enter your new character's name: "
        if state == states.RENAMING_NEW_CHARACTER:
            return "That name is unavailable, please choose another: "
        if state == states.SETTING_PASSWORD:
            return "Choose a password: "

    def to_self(self, message, prompt=True, prepend_newline=False, *args, **kwargs):
        if prepend_newline:
            self.transport.write("\n")
        self.transport.write("%s\n" %Color.colorize(message))
        if prompt:
            self.do_prompt()

    def do_prompt(self, newline=False):
        if newline:
            self.transport.write("\n" + self.prompt)
        else:
            self.transport.write(self.prompt)

    def is_logged_in(self):
        return self.state == states.PLAYING

    def die(self):
        GameState.drop_connection(self)

@Interpreter.command('chat')
def chat( ch, args ):
    """
    Usage:
    chat <message>

    Broadcasts a message to all players.
    """
    GameState.to_all(
        "${blue,bold}%s chats '${white,bold}%s${blue,bold}'" % (ch.username,
        ' '.join(args)), ch.connection.transport)

